
local ffi = require("ffi")

ffi.cdef[[
	typedef unsigned int    UINT;
	typedef unsigned int    DWORD;
	typedef unsigned short  WORD;
	typedef unsigned short  WCHAR;
	typedef unsigned short  wchar_t;
	typedef void            FILE;
	typedef void*           HANDLE;
	typedef bool            BOOL;
]]

ffi.cdef[[
	int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr , int cchWideChar);
	int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, wchar_t*    lpWideCharStr , int cchWideChar, char*    lpMultiByteStr, int cbMultiByte, char* lpDefaultChar, bool* lpUsedDefaultChar);
]]
function towchar(strb)
	local osize = #strb + 1
	local strw = ffi.new("wchar_t["..osize.."]")
	ffi.C.MultiByteToWideChar(65001, 0, strb, -1, strw, osize)
	return strw
end
function fromwchar(strw)
	local osize = ffi.sizeof(strw)*4 + 1
	local strb = ffi.new("char["..osize.."]")
	ffi.C.WideCharToMultiByte(65001, 0, strw, -1, strb, osize, ffi.cast("char*", 0), ffi.cast("bool*", 0))
	return ffi.string(strb)
end

ffi.cdef("int _wsystem(wchar_t* command)")
function execw(cmd)
	local cmdw = towchar(cmd)
	ffi.C._wsystem(cmdw)
end

ffi.cdef[[
	FILE*    _wpopen(wchar_t* command, wchar_t* mode);
	wchar_t* fgetws (wchar_t* str, int numChars, FILE* stream);
	int      feof   (FILE* stream);
	int      _pclose(FILE* stream);
]]
function rexecw(cmd)
	cmd = cmd.." 2>&1" -- pipe stderr to stdout
	local cmdw = towchar(cmd)
	local modew = towchar("r")
	local file = ffi.C._wpopen(cmdw, modew)
	
	local stro = ""
	local buffer = ffi.new("wchar_t[256]")
	while ffi.C.feof(file)==0 do
		ffi.C.fgetws(buffer, 256, file)
		local str = fromwchar(buffer)
		stro = stro..str
	end
	
	ffi.C._pclose(file)
	
	return stro
end

ffi.cdef("bool SetCurrentDirectoryW(wchar_t* lpPathName)")
function cdw(dir)
	local dirw = towchar(dir)
	ffi.C.SetCurrentDirectoryW(dirw)
end

ffi.cdef[[
	typedef struct _FILETIME {
		DWORD     dwLowDateTime;
		DWORD     dwHighDateTime;
	} FILETIME, *PFILETIME, *LPFILETIME;
	typedef struct _WIN32_FIND_DATAW {
		DWORD     dwFileAttributes;
		FILETIME  ftCreationTime;
		FILETIME  ftLastAccessTime;
		FILETIME  ftLastWriteTime;
		DWORD     nFileSizeHigh;
		DWORD     nFileSizeLow;
		DWORD     dwReserved0;
		DWORD     dwReserved1;
		WCHAR     cFileName[0x00000104];
		WCHAR     cAlternateFileName[14];
		DWORD     dwFileType;
		DWORD     dwCreatorType;
		WORD      wFinderFlags;
	} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
	HANDLE  FindFirstFileW(wchar_t* lpFileName, LPWIN32_FIND_DATAW lpFindFileData);
	BOOL    FindNextFileW (HANDLE   hFindFile , LPWIN32_FIND_DATAW lpFindFileData);
]]
function findfilesw(dir)
	local dirw = towchar(dir)
	local infos = {}
	
	local finfo = ffi.new("WIN32_FIND_DATAW")
	local hfind = ffi.C.FindFirstFileW(dirw, finfo)
	while true do
		table.insert(infos, finfo)
		finfo = ffi.new("WIN32_FIND_DATAW")
		local next = ffi.C.FindNextFileW(hfind, finfo)
		if not next then break end
	end
	
	return infos
end
function findfilenamesw(dir)
	local names = {}
	local infos = findfilesw(dir)
	for _, info in ipairs(infos) do
		if bit.band(info.dwFileAttributes, 0x10)==0 then -- if not a directory
			table.insert(names, fromwchar(info.cFileName))
		end
	end
	return names
end
function findfilenamesanddirsw(dir)
	local names = {}
	local infos = findfilesw(dir)
	for _, info in ipairs(infos) do
		if bit.band(info.dwFileAttributes, 0x10)==0 then -- if not a directory
			table.insert(names, fromwchar(info.cFileName))
		else
			local fn = fromwchar(info.cFileName)
			if fn~="." and fn~=".." then
				table.insert(names, fn.."/")
			end
		end
	end
	return names
end
